// TopRendererWnd.cpp: implementation of the C2DViewport class.
//
//////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include "2DViewport.h"
#include "CryEditDoc.h"

#include "Grid.h"
#include "DisplaySettings.h"
#include "ViewManager.h"

// Include OpenGL
//#include "GL\gl.h"
#include "Brush\Brush.h"
#include "Objects\BrushObject.h"

#include <IRenderAuxGeom.h>

#define MARKER_SIZE 6.0f
#define MARKER_DIR_SIZE 10.0f
#define SELECTION_RADIUS 30.0f

#define GL_RGBA 0x1908
#define GL_BGRA 0x80E1

#define BACKGROUND_COLOR Vec3(1.0f,1.0f,1.0f)
#define SELECTION_RECT_COLOR Vec3(0.8f,0.8f,0.8f)
#define MINOR_GRID_COLOR	Vec3(0.55f,0.55f,0.55f)
#define MAJOR_GRID_COLOR	Vec3(0.6f,0.6f,0.6f)
#define AXIS_GRID_COLOR		Vec3(0,0,0)
#define GRID_TEXT_COLOR		Vec3(0,0,1.0f)

#define MAX_WORLD_SIZE 10000

enum Viewport2dTitleMenuCommands
{
	ID_SHOW_LABELS,
	ID_SHOW_GRID
};


//////////////////////////////////////////////////////////////////////////
IMPLEMENT_DYNCREATE(C2DViewport,CViewport)
IMPLEMENT_DYNCREATE(C2DViewport_XY,C2DViewport)
IMPLEMENT_DYNCREATE(C2DViewport_XZ,C2DViewport)
IMPLEMENT_DYNCREATE(C2DViewport_YZ,C2DViewport)

//////////////////////////////////////////////////////////////////////////
BEGIN_MESSAGE_MAP(C2DViewport, CViewport)
	//{{AFX_MSG_MAP(C2DViewport)
	ON_WM_LBUTTONDOWN()
	ON_WM_MBUTTONDOWN()
	ON_WM_MBUTTONUP()
	ON_WM_LBUTTONUP()
	ON_WM_MOUSEMOVE()
	ON_WM_SIZE()
	ON_WM_ERASEBKGND()
	ON_WM_PAINT()
	ON_WM_CREATE()
	ON_WM_RBUTTONDOWN()
	ON_WM_RBUTTONUP()
	ON_WM_MOUSEWHEEL()
	//}}AFX_MSG_MAP
	ON_WM_DESTROY()
END_MESSAGE_MAP()

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
C2DViewport::C2DViewport()
{
	// Scroll offset equals origin
	m_rcSelect.SetRect( 0,0,0,0 );

	m_viewType = ET_ViewportXY;
	m_axis = VPA_XY;

	m_bShowTerrain = true;
	m_gridAlpha = 1;

	m_origin2D.Set(0,0,0);

	//m_colorGridText = RGB(0,0,255);
	//m_colorAxisText = RGB(0,0,0);
	//m_colorBackground = Vec2Rgb(BACKGROUND_COLOR);
	//m_gridAlpha = 0.3f;
	m_colorGridText = RGB(220,220,220);
	m_colorAxisText = RGB(220,220,220);
	m_colorBackground = RGB(128,128,128);

	m_screenTM.SetIdentity();
	m_screenTM_Inverted.SetIdentity();

	m_fZoomFactor = 1;
	m_bContentValid = false;

	m_bShowViewMarker = true;

	m_bProcessingOnPaintMsg = false;

	//////////////////////////////////////////////////////////////////////////
	// Create window.
	CreateViewportWindow();
}

//////////////////////////////////////////////////////////////////////////
C2DViewport::~C2DViewport()
{
}

//////////////////////////////////////////////////////////////////////////
void C2DViewport::SetType( EViewportType type )
{
	//assert( type == ET_ViewportXY || type == ET_ViewportXZ || type == ET_ViewportYZ );
	m_viewType = type;

	switch (m_viewType)
	{
	case ET_ViewportXY:
		m_axis = VPA_XY;
		break;
	case ET_ViewportXZ:
		m_axis = VPA_XZ;
		break;
	case ET_ViewportYZ:
		m_axis = VPA_YZ;
		break;
	};

	SetAxis( m_axis );
}

//////////////////////////////////////////////////////////////////////////
void C2DViewport::SetAxis( EViewportAxis axis )
{
	m_axis = axis;
	switch (m_axis)
	{
	case VPA_XY:
		m_cullAxis = 2;
		break;
	case VPA_YX:
		m_cullAxis = 2;
		break;
	case VPA_XZ:
		m_cullAxis = 1;
		break;
	case VPA_YZ:
		m_cullAxis = 0;
		break;
	}
}

//////////////////////////////////////////////////////////////////////////
void C2DViewport::CalculateViewTM()
{
	Matrix34 viewTM;
	Matrix34 tm;
	tm.SetIdentity();
	viewTM.SetIdentity();
	float fScale = GetZoomFactor();
	Vec3 origin = GetOrigin2D();
	//float origin[2] = { -m_cScrollOffset.x,-m_cScrollOffset.y };

	float height = m_rcClient.Height()/fScale;

	Vec3 v1;
	switch (m_axis)
	{
	case VPA_XY:
		//tm = Matrix33::CreateScale( Vec3fScale,-fScale,fScale) ) * tm;	// No fScale for Z
		//tm.SetTranslation( Vec3(-origin.x,height+origin.y,0)*fScale );


		tm = Matrix33::CreateScale( Vec3(fScale,-fScale,fScale) ) * tm;	// No fScale for Z
		tm.SetTranslation( Vec3(-origin.x,height+origin.y,0)*fScale );
		break;
	case VPA_YX:
		//viewTM.SetFromVectors( Vec3(0,1,0),Vec3(1,0,0),Vec3(0,0,1),Vec3(0,0,0) );
		//tm = Matrix33::CreateScale( Vec3(fScale,fScale,fScale) ) * tm;	// No fScale for Z
		tm.SetFromVectors( Vec3(0,1,0)*fScale,Vec3(1,0,0)*fScale,Vec3(0,0,1)*fScale,Vec3(0,0,0) );
		//tm.SetTranslation( Vec3(-origin.y,height+origin.x,0)*fScale );
		tm.SetTranslation( Vec3(-origin.x,height+origin.y,0)*fScale );
		break;
	case VPA_XZ:
		viewTM.SetFromVectors( Vec3(1,0,0),Vec3(0,0,1),Vec3(0,1,0),Vec3(0,0,0) );
		tm.SetFromVectors( Vec3(1,0,0)*fScale,Vec3(0,0,1)*fScale,Vec3(0,-1,0)*fScale,Vec3(0,0,0) );
		tm.SetTranslation( Vec3(-origin.x,height+origin.z,0)*fScale );
		break;

	case VPA_YZ:
		viewTM.SetFromVectors( Vec3(0,1,0),Vec3(0,0,1),Vec3(1,0,0),Vec3(0,0,0) );
		tm.SetFromVectors( Vec3(0,0,1)*fScale,Vec3(1,0,0)*fScale,Vec3(0,-1,0)*fScale,Vec3(0,0,0) ); // No fScale for Z
		tm.SetTranslation( Vec3(-origin.y,height+origin.z,0)*fScale );
		break;
	}
	SetViewTM( viewTM );
	m_screenTM = tm;
	m_screenTM_Inverted = m_screenTM.GetInverted();

	SetConstructionMatrix( COORDS_VIEW,GetViewTM() );
}

//////////////////////////////////////////////////////////////////////////
void C2DViewport::ResetContent()
{
	CViewport::ResetContent();
}

//////////////////////////////////////////////////////////////////////////
void C2DViewport::UpdateContent( int flags )
{
	CViewport::UpdateContent(flags);
	if (flags & eUpdateObjects)
	{
		m_bContentValid = false;
	}
}

//////////////////////////////////////////////////////////////////////////
void C2DViewport::OnRButtonDown(UINT nFlags, CPoint point) 
{
	if (GetIEditor()->IsInGameMode())
		return;

	if (GetFocus() != this)
		SetFocus();

	// Check Edit Tool.
	MouseCallback( eMouseRDown,point,nFlags );

	m_bRMouseDown = true;

	SetCurrentCursor( STD_CURSOR_MOVE,"" );

	// Save the mouse down position
	m_RMouseDownPos = point;

	m_prevZoomFactor = GetZoomFactor();
	//m_prevScrollOffset = m_cScrollOffset;

	CaptureMouse();
	SetViewMode( ScrollZoomMode );

	CWnd::OnRButtonDown(nFlags, point);

	m_bContentValid = false;
}

//////////////////////////////////////////////////////////////////////////
void C2DViewport::OnRButtonUp(UINT nFlags, CPoint point) 
{
	// Check Edit Tool.
	//if (MouseCallback( eMouseRUp,point,nFlags ))
	//return;

	m_bRMouseDown = false;
	if (GetCapture() == this)
		ReleaseMouse();
	SetViewMode( NothingMode );

	CWnd::OnRButtonUp(nFlags, point);

	GetViewManager()->UpdateViews(eUpdateObjects);
}

//////////////////////////////////////////////////////////////////////////
void C2DViewport::OnMButtonDown(UINT nFlags, CPoint point)
{
	////////////////////////////////////////////////////////////////////////
	// User pressed the middle mouse button
	////////////////////////////////////////////////////////////////////////
	// Check Edit Tool.
	if (MouseCallback( eMouseMDown,point,nFlags ))
		return;

	CWnd::OnMButtonDown(nFlags, point);

	// Save the mouse down position
	m_RMouseDownPos = point;

	m_bRMouseDown = true;

	SetCurrentCursor( STD_CURSOR_MOVE,"" );

	// Save the mouse down position
	m_RMouseDownPos = point;

	m_prevZoomFactor = GetZoomFactor();
	//m_prevScrollOffset = m_cScrollOffset;

	CaptureMouse();
	SetViewMode( ScrollZoomMode );

	CaptureMouse();
	m_bContentValid = false;
}

//////////////////////////////////////////////////////////////////////////
void C2DViewport::OnMButtonUp(UINT nFlags, CPoint point)
{
	// Check Edit Tool.
	if (MouseCallback( eMouseMUp,point,nFlags ))
		return;

	m_bRMouseDown = false;
	ReleaseMouse();
	SetViewMode( NothingMode );

	ReleaseMouse();
	m_bContentValid = false;
	GetViewManager()->UpdateViews(eUpdateObjects);

	CWnd::OnMButtonUp(nFlags, point);
}

//////////////////////////////////////////////////////////////////////////
void C2DViewport::OnLButtonDown(UINT nFlags, CPoint point)
{
	////////////////////////////////////////////////////////////////////////
	// User pressed the left mouse button
	////////////////////////////////////////////////////////////////////////
	if (GetViewMode() != NothingMode)
		return;

	m_cMouseDownPos = point;
	m_prevZoomFactor = GetZoomFactor();
	//m_prevScrollOffset = m_cScrollOffset;

	CViewport::OnLButtonDown(nFlags, point);
	m_bContentValid = false;
}

//////////////////////////////////////////////////////////////////////////
void C2DViewport::OnLButtonUp(UINT nFlags, CPoint point)
{
	////////////////////////////////////////////////////////////////////////
	// Process the various events depending on the selection and the view 
	// mode
	////////////////////////////////////////////////////////////////////////
	CViewport::OnLButtonUp(nFlags, point);

	GetViewManager()->UpdateViews(eUpdateObjects);
}

//////////////////////////////////////////////////////////////////////////
BOOL C2DViewport::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt) 
{
	float z = GetZoomFactor();
	float scale = 1.2f * fabs(zDelta/120.0f);
	if (zDelta >= 0)
	{
		z = z * scale;
	}
	else
	{
		z = z / scale;
	}
	SetZoom( z,m_cMousePos );

	GetViewManager()->UpdateViews(eUpdateObjects);

	return TRUE;
}

//////////////////////////////////////////////////////////////////////////
void C2DViewport::OnMouseMove(UINT nFlags, CPoint point)
{	
	m_cMousePos = point;

	if (GetViewMode() == ScrollZoomMode)
	{
		CRect rc;
		// You can only scroll while the middle mouse button is down
		if (nFlags & MK_RBUTTON || nFlags & MK_MBUTTON)
		{
			if (nFlags & MK_SHIFT)
			{
				// Get the dimensions of the window
				GetClientRect(&rc);

				CRect rc;
				GetClientRect( rc );
				int w = rc.right;
				int h = rc.bottom;

				// Zoom to mouse position.
				float z = m_prevZoomFactor + (point.y - m_RMouseDownPos.y) * 0.02f;
				SetZoom( z,m_RMouseDownPos );
			}
			else
			{
				// Set the new scrolled coordinates
				float fScale = GetZoomFactor();
				float ofsx,ofsy;
				GetScrollOffset( ofsx,ofsy );
				ofsx -= (point.x - m_RMouseDownPos.x)/fScale;
				ofsy += (point.y - m_RMouseDownPos.y)/fScale;
				SetScrollOffset( ofsx,ofsy );
				m_RMouseDownPos = point;
			}
		}
		return;
	}

	CViewport::OnMouseMove(nFlags, point);

	m_bContentValid = false;
}

//////////////////////////////////////////////////////////////////////////
void C2DViewport::SetScrollOffset( float x,float y,bool bLimits )
{
	Vec3 org = GetOrigin2D();
	switch (m_axis)
	{
	case VPA_XY:
	case VPA_YX:
		{
			/*
			if (bLimits)
			{
			float maxMapSize = GetIEditor()->GetHeightmap()->GetUnitSize()*GetIEditor()->GetHeightmap()->GetWidth();
			// Limit scroll offsets.
			x = max(x,-maxMapSize-maxMapSize/2);
			y = max(y,-maxMapSize-maxMapSize/2);
			x = min(x,maxMapSize/2);
			y = min(y,maxMapSize/2);
			}
			*/
		}
		org.x = x;	org.y = y;
		break;
	case VPA_XZ:
		org.x = x;	org.z = y;
		break;
	case VPA_YZ:
		org.y = x;	org.z = y;
		break;
	}

	SetOrigin2D(org);

	CalculateViewTM();
	//GetViewManager()->UpdateViews(eUpdateObjects);
	m_bContentValid = false;
}

//////////////////////////////////////////////////////////////////////////
void C2DViewport::GetScrollOffset( float &x,float &y )
{
	Vec3 origin = GetOrigin2D();
	switch (m_axis)
	{
	case VPA_XY:
	case VPA_YX:
		x = origin.x;
		y = origin.y;
		break;
	case VPA_XZ:
		x = origin.x;
		y = origin.z;
		break;
	case VPA_YZ:
		x = origin.y;
		y = origin.z;
		break;
	}
}

//////////////////////////////////////////////////////////////////////////
void C2DViewport::SetZoom( float fZoomFactor,CPoint center )
{
	if (fZoomFactor < 0.01f)
		fZoomFactor = 0.01f;

	float prevz = GetZoomFactor();

	// Zoom to mouse position.
	float ofsx,ofsy;
	GetScrollOffset( ofsx,ofsy );

	float s1 = GetZoomFactor();
	float s2 = fZoomFactor;

	SetZoomFactor( fZoomFactor );

	// Calculate new offset to center zoom on mouse.
	float x2 = center.x;
	float y2 = m_rcClient.Height() - center.y;
	ofsx = -(x2/s2 - x2/s1 - ofsx);
	ofsy = -(y2/s2 - y2/s1 - ofsy);
	SetScrollOffset( ofsx,ofsy,true );

	CalculateViewTM();

	//GetViewManager()->UpdateViews(eUpdateObjects);
	m_bContentValid = false;
}

//////////////////////////////////////////////////////////////////////////
afx_msg void C2DViewport::OnSize(UINT nType, int cx, int cy)
{
	////////////////////////////////////////////////////////////////////////
	// Re-evaluate the zoom / scroll offset values
	// TODO: Restore the zoom rectangle instead of resetting it
	////////////////////////////////////////////////////////////////////////

	CViewport::OnSize(nType, cx, cy);

	GetClientRect( &m_rcClient );
	CalculateViewTM();
}

//////////////////////////////////////////////////////////////////////////
void C2DViewport::OnPaint() 
{
	{
		CPaintDC dc(this); // device context for painting

		//CBrush cFillBrush;
		//CRect rect;
		//GetClientRect(&rect);
		//cFillBrush.CreateSolidBrush(RGB(128,128,128));
		//dc.FillRect(&rect, &cFillBrush);
	}

	//Draw( dc,dc.m_ps.rcPaint );
	if (!m_bProcessingOnPaintMsg)
	{
		m_bProcessingOnPaintMsg = true;
		Render();
		m_bProcessingOnPaintMsg = false;
	}
}

//////////////////////////////////////////////////////////////////////////
BOOL C2DViewport::OnEraseBkgnd(CDC* pDC) 
{
	return TRUE;
}

//////////////////////////////////////////////////////////////////////////
int C2DViewport::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
	if (CViewport::OnCreate(lpCreateStruct) == -1)
		return -1;

	m_renderer = GetIEditor()->GetRenderer();
	assert (m_renderer != NULL);
	if (m_renderer)
	{
		m_renderer->CreateContext( m_hWnd );
		m_renderer->MakeMainContextActive();
	}

	// Calculate the View transformation matrix.
	CalculateViewTM();

	return 0;
}

//////////////////////////////////////////////////////////////////////////
void C2DViewport::Update()
{
	if (!m_bContentValid)
		Render();	
	m_bContentValid = true;
	CViewport::Update();
}

//////////////////////////////////////////////////////////////////////////
CPoint C2DViewport::WorldToView( Vec3 wp )
{
	wp = m_screenTM.TransformPoint(wp);
	CPoint p = CPoint(wp.x,wp.y);
	return p;
}

//////////////////////////////////////////////////////////////////////////
Vec3	C2DViewport::ViewToWorld( CPoint vp,bool *collideWithTerrain,bool onlyTerrain, bool bSkipVegetation, bool bTestRenderMesh )
{
	Vec3 wp = m_screenTM_Inverted.TransformPoint( Vec3(vp.x,vp.y,0) );
	switch (m_axis)
	{
	case VPA_XY:
	case VPA_YX:
		wp.z = 0;
		break;
	case VPA_XZ:
		wp.y = 0;
		break;
	case VPA_YZ:
		wp.x = 0;
		break;
	}
	return wp;
}

//////////////////////////////////////////////////////////////////////////
void	C2DViewport::ViewToWorldRay( CPoint vp,Vec3 &raySrc,Vec3 &rayDir )
{
	raySrc = ViewToWorld( vp );
	switch (m_axis)
	{
	case VPA_XY:
	case VPA_YX:
		raySrc.z = MAX_WORLD_SIZE;
		rayDir(0,0,-1);
		break;
	case VPA_XZ:
		raySrc.y = MAX_WORLD_SIZE;
		rayDir(0,-1,0);
		break;
	case VPA_YZ:
		raySrc.x = MAX_WORLD_SIZE;
		rayDir(-1,0,0);
		break;
	}
}

//////////////////////////////////////////////////////////////////////////
float C2DViewport::GetScreenScaleFactor( const Vec3 &worldPoint )
{
	return 400.0f / GetZoomFactor();
	//return 100.0f / ;
}

//////////////////////////////////////////////////////////////////////////
void C2DViewport::OnTitleMenu( CMenu &menu )
{
	bool labels = GetIEditor()->GetDisplaySettings()->IsDisplayLabels();
	bool bGrid = GetIEditor()->GetViewManager()->GetGrid()->bEnabled;
	menu.AppendMenu( MF_STRING|((labels)?MF_CHECKED:MF_UNCHECKED), ID_SHOW_LABELS, "Labels" );
	menu.AppendMenu( MF_STRING|((bGrid)?MF_CHECKED:MF_UNCHECKED), ID_SHOW_GRID ,"Grid" );
}

//////////////////////////////////////////////////////////////////////////
void C2DViewport::OnTitleMenuCommand( int id )
{
	if ( id == ID_SHOW_LABELS )
	{
		GetIEditor()->GetDisplaySettings()->DisplayLabels( !GetIEditor()->GetDisplaySettings()->IsDisplayLabels() );
	}
	else if ( id == ID_SHOW_GRID )
	{
		CGrid *grid = GetIEditor()->GetViewManager()->GetGrid();
		grid->Enable( !grid->IsEnabled() );
	}
}

//////////////////////////////////////////////////////////////////////////
void C2DViewport::OnDestroy()
{
	if (m_renderer)
		m_renderer->DeleteContext( m_hWnd );
	CViewport::OnDestroy();
}

//////////////////////////////////////////////////////////////////////////
void C2DViewport::Render()
{
	if (GetIEditor()->IsInGameMode())
		return;

	if (!m_renderer)
		return;

	if (!IsWindowVisible())
		return;

	if (!GetIEditor()->GetDocument()->IsDocumentReady())
		return;

	FUNCTION_PROFILER( GetIEditor()->GetSystem(),PROFILE_EDITOR );

	CRect rc;
	GetClientRect( rc );

	CalculateViewTM();

	// Render
	m_renderer->SetCurrentContext( m_hWnd );
	m_renderer->BeginFrame();
	m_renderer->ChangeViewport(0,0,rc.right,rc.bottom,true);
	m_renderer->SetWireframeMode( R_SOLID_MODE );
	Vec3 clCol = Rgb2Vec(m_colorBackground);
	m_renderer->ClearBuffer(FRT_CLEAR, &ColorF(clCol, 1.0f));

	//////////////////////////////////////////////////////////////////////////
	// 2D Mode.
	//////////////////////////////////////////////////////////////////////////
	if (m_rcClient.right != 0 && m_rcClient.bottom != 0)
	{
		m_renderer->Set2DMode( true,m_rcClient.right,m_rcClient.bottom );
		//Matrix34 vtm = GetViewTM();
		//m_renderer->LoadMatrix( &vtm );

		//////////////////////////////////////////////////////////////////////////
		// Draw viewport elements here.
		//////////////////////////////////////////////////////////////////////////
		// Calc world bounding box for objects rendering.
		m_displayBounds = GetWorldBounds( CPoint(0,0),CPoint(m_rcClient.Width(),m_rcClient.Height()) );

		// Draw all objects.
		DisplayContext &dc = m_displayContext;
		dc.settings = GetIEditor()->GetDisplaySettings();
		dc.view = this;
		dc.renderer = m_renderer;
		dc.engine = GetIEditor()->Get3DEngine();
		dc.flags = DISPLAY_2D;
		dc.box = m_displayBounds;
		dc.camera = &GetIEditor()->GetSystem()->GetViewCamera();

		if (!dc.settings->IsDisplayLabels() || !dc.settings->IsDisplayHelpers())
		{
			dc.flags |= DISPLAY_HIDENAMES;
		}
		if (dc.settings->IsDisplayLinks() && dc.settings->IsDisplayHelpers())
		{
			dc.flags |= DISPLAY_LINKS;
		}
		if (m_bDegradateQuality)
		{
			dc.flags |= DISPLAY_DEGRADATED;
		}

		m_renderer->EF_StartEf();

		//m_renderer->EnableDepthTest(false);
		//m_renderer->EnableDepthWrites(false);
		dc.SetState( e_Mode3D|e_AlphaBlended|e_FillModeSolid|e_CullModeBack|e_DepthWriteOff|e_DepthTestOn );
		//dc.DepthWriteOff();
		Draw( dc );
		//dc.DepthTestOn();
		//m_renderer->EnableDepthTest(true);
		//m_renderer->EnableDepthWrites(true);
		m_renderer->EF_EndEf3D(SHDF_STREAM_SYNC, -1, -1);

		m_renderer->FlushTextMessages();

		// Return back from 2D mode.
		m_renderer->Set2DMode( false,m_rcClient.right,m_rcClient.bottom );

		m_renderer->RenderDebug(false);

		ProcessRenderLisneters(m_displayContext);

		m_renderer->EndFrame();

	}


	GetIEditor()->GetRenderer()->MakeMainContextActive();
}

//////////////////////////////////////////////////////////////////////////
void C2DViewport::Draw( DisplayContext &dc )
{
	dc.DepthTestOff();
	dc.DepthWriteOff();
	DrawGrid(dc);
	dc.DepthTestOn();
	dc.DepthWriteOff();

	m_renderer->GetIRenderAuxGeom()->Flush();

	DrawObjects(dc);
	DrawSelection(dc);
	if (m_bShowViewMarker)
		DrawViewerMarker(dc);
	DrawAxis(dc);
}

//////////////////////////////////////////////////////////////////////////
inline Vec3 SnapToSize( Vec3 v,double size )
{
	Vec3 snapped;
	snapped.x = floor((v.x/size) + 0.5) * size;
	snapped.y = floor((v.y/size) + 0.5) * size;
	snapped.z = floor((v.z/size) + 0.5) * size;
	return snapped;
}

//////////////////////////////////////////////////////////////////////////
void C2DViewport::DrawGrid( DisplayContext &dc,bool bNoXNumbers )
{
	CGrid *pGrid = GetIEditor()->GetViewManager()->GetGrid();
	double gridSize = pGrid->size;

	if (gridSize < 0.00001)
		return;

	float fZ = 0;

	//////////////////////////////////////////////////////////////////////////
	bool bShowGrid = pGrid->IsEnabled();

	///if (!bShowGrid)
	//return;

	float fScale = GetZoomFactor();

	int width = m_rcClient.Width();
	int height = m_rcClient.Height();

	float origin[2];
	GetScrollOffset( origin[0],origin[1] );

	//////////////////////////////////////////////////////////////////////////
	// Draw Minor grid lines.
	//////////////////////////////////////////////////////////////////////////

	float pixelsPerGrid = gridSize*fScale;

	m_fGridZoom = 1.0f;

	int griditers = 0;
	pixelsPerGrid = gridSize*fScale;
	while (pixelsPerGrid <= 5 && griditers++ < 20)
	{
		m_fGridZoom *= pGrid->majorLine;
		gridSize = gridSize*pGrid->majorLine;
		pixelsPerGrid = gridSize*fScale;
	}

	int firstGridLineX = origin[0] / gridSize - 1;
	int firstGridLineY = origin[1] / gridSize - 1;

	int numGridLinesX = (m_rcClient.Width()/fScale) / gridSize + 1;
	int numGridLinesY = (m_rcClient.Height()/fScale) / gridSize + 1;

	Matrix34 viewTM = GetViewTM().GetInverted() * m_screenTM_Inverted;
	Matrix34 viewTM_Inv = m_screenTM * GetViewTM();

	Vec3 viewP0 = viewTM.TransformPoint( Vec3(0,0,0) );
	Vec3 viewP1 = viewTM.TransformPoint( Vec3(m_rcClient.Width(),m_rcClient.Height(),0) );

	Vec3 viewP_Text = viewTM.TransformPoint( Vec3(2,2,0) );

	if (pixelsPerGrid > 5)
	{
		//////////////////////////////////////////////////////////////////////////
		// Draw Minor grid lines.
		//////////////////////////////////////////////////////////////////////////

		Vec3 vec0 = SnapToSize(viewP0,gridSize);
		Vec3 vec1 = SnapToSize(viewP1,gridSize);
		if (vec0.x > vec1.x)
			std::swap(vec0.x,vec1.x);
		if (vec0.y > vec1.y)
			std::swap(vec0.y,vec1.y);

		dc.SetColor( MINOR_GRID_COLOR,m_gridAlpha );
		for (double dy = vec0.y; dy < vec1.y; dy += gridSize)
		{
			Vec3 p0 = viewTM_Inv.TransformPoint( Vec3(viewP0.x,dy,0) );
			Vec3 p1 = viewTM_Inv.TransformPoint( Vec3(viewP1.x,dy,0) );
			dc.DrawLine( Vec3(p0.x,p0.y,fZ),Vec3(p1.x,p1.y,fZ) );
		}
		for (double dx = vec0.x; dx < vec1.x; dx += gridSize)
		{
			Vec3 p0 = viewTM_Inv.TransformPoint( Vec3(dx,viewP0.y,0) );
			Vec3 p1 = viewTM_Inv.TransformPoint( Vec3(dx,viewP1.y,0) );
			dc.DrawLine( Vec3(p0.x,p0.y,fZ),Vec3(p1.x,p1.y,fZ) );
		}
	}
	{
		//////////////////////////////////////////////////////////////////////////
		// Draw Major grid lines.
		//////////////////////////////////////////////////////////////////////////
		gridSize = gridSize * pGrid->majorLine;

		int iters = 0;
		pixelsPerGrid = gridSize*fScale;
		while (pixelsPerGrid < 20 && iters < 20)
		{
			gridSize = gridSize*2;
			pixelsPerGrid = gridSize*fScale;
			iters++;
		}
		if (pixelsPerGrid > 20)
		{
			Vec3 vec0 = SnapToSize(viewP0,gridSize);
			Vec3 vec1 = SnapToSize(viewP1,gridSize);
			if (vec0.x > vec1.x)
				std::swap(vec0.x,vec1.x);
			if (vec0.y > vec1.y)
				std::swap(vec0.y,vec1.y);

			dc.SetColor( MAJOR_GRID_COLOR,m_gridAlpha );

			for (double dy = vec0.y; dy < vec1.y; dy += gridSize)
			{
				Vec3 p0 = viewTM_Inv.TransformPoint( Vec3(viewP0.x,dy,0) );
				Vec3 p1 = viewTM_Inv.TransformPoint( Vec3(viewP1.x,dy,0) );
				dc.DrawLine( Vec3(p0.x,p0.y,fZ),Vec3(p1.x,p1.y,fZ) );
			}
			for (double dx = vec0.x; dx < vec1.x; dx += gridSize)
			{
				Vec3 p0 = viewTM_Inv.TransformPoint( Vec3(dx,viewP0.y,0) );
				Vec3 p1 = viewTM_Inv.TransformPoint( Vec3(dx,viewP1.y,0) );
				dc.DrawLine( Vec3(p0.x,p0.y,fZ),Vec3(p1.x,p1.y,fZ) );
			}

			// Draw numbers.
			{
				char text[64];
				dc.SetColor( m_colorGridText );

				if (!bNoXNumbers)
				{
					// Draw horizontal grid text.
					for (double dx = vec0.x; dx < vec1.x; dx += gridSize)
					{
						Vec3 p = viewTM_Inv.TransformPoint( Vec3(dx,viewP_Text.y,0) );
						sprintf( text, "%i",(int)(dx) );
						dc.Draw2dTextLabel( p.x,p.y,1,text );
					}
				}
				for (double dy = vec0.y; dy < vec1.y; dy += gridSize)
				{
					Vec3 p = viewTM_Inv.TransformPoint( Vec3(viewP_Text.x,dy,0) );
					sprintf( text, "%i",(int)(dy) );
					dc.Draw2dTextLabel( p.x,p.y,1,text );
				}
			}
		}
	}

	// Draw Main Axis.
	{
		Vec3 org = m_screenTM.TransformPoint( Vec3(0,0,0) );
		dc.SetColor( AXIS_GRID_COLOR );
		dc.DrawLine( Vec3(org.x,0,fZ),Vec3(org.x,height,fZ) );
		dc.DrawLine( Vec3(0,org.y,fZ),Vec3(width,org.y,fZ) );
	}
	//////////////////////////////////////////////////////////////////////////
}

//////////////////////////////////////////////////////////////////////////
void C2DViewport::DrawAxis( DisplayContext &dc )
{
	int ix = 0;
	int iy = 0;
	float cl = 0.85f;
	char xstr[2],ystr[2],zstr[2];
	Vec3 colx,coly,colz;
	Vec3 colorX(cl,0,0);
	Vec3 colorY(0,cl,0);
	Vec3 colorZ(0,0,cl);
	switch (m_axis)
	{
	case VPA_XY:
		strcpy( xstr,"x" );
		strcpy( ystr,"y" );
		strcpy( zstr,"z" );
		colx = colorX;
		coly = colorY;
		colz = colorZ;
		break;
	case VPA_YX:
		strcpy( xstr,"x" );
		strcpy( ystr,"y" );
		strcpy( zstr,"z" );
		colx = colorY;
		coly = colorX;
		colz = colorZ;
		break;
	case VPA_XZ:
		strcpy( xstr,"x" );
		strcpy( ystr,"z" );
		strcpy( zstr,"y" );
		colx = colorX;
		coly = colorZ;
		colz = colorY;
		break;
	case VPA_YZ:
		strcpy( xstr,"y" );
		strcpy( ystr,"z" );
		strcpy( zstr,"x" );
		colx = colorY;
		coly = colorZ;
		colz = colorX;
		break;
	}

	int width = m_rcClient.Width();
	int height = m_rcClient.Height();

	int size = 25;
	Vec3 pos( 30,height-15,1 );

	dc.SetColor( colx.x,colx.y,colx.z,1 );
	dc.DrawLine( pos,pos + Vec3(size,0,0) );

	dc.SetColor( coly.x,coly.y,coly.z,1 );
	dc.DrawLine( pos,pos - Vec3(0,size,0) );


	//m_renderer->SetMaterialColor( 0,0,1,1 );
	//m_renderer->DrawLine( pos,pos + Vec3(0,0,20) );
	//gl.PrintText( pos.x+size,pos.y,"x" );

	dc.SetColor( m_colorAxisText );
	pos.x -= 3;
	pos.y -= 4;
	pos.z = 2;
	dc.Draw2dTextLabel( pos.x+size+4,pos.y-2,1,xstr );
	dc.Draw2dTextLabel( pos.x+3,pos.y-size,1,ystr );
	dc.Draw2dTextLabel( pos.x-5,pos.y+5,1,zstr );
}

//////////////////////////////////////////////////////////////////////////
void C2DViewport::DrawSelection( DisplayContext &dc )
{
	AABB box;
	GetIEditor()->GetSelectedRegion( box );
	if (!IsEquivalent(box.min,box.max,0))
	{
		//m_renderer->SetMaterialColor( SELECTION_RECT_COLOR.x,SELECTION_RECT_COLOR.y,SELECTION_RECT_COLOR.z,1 ); // white
		//m_renderer->DrawWireB
		/*
		CRect rc;
		CPoint p1 = WorldToView( box.min );
		CPoint p2 = WorldToView( box.max );
		rc.SetRect( p1,p2 );
		rc.NormalizeRect();
		// Draw selection.
		if (rc.left != rc.right || rc.top != rc.bottom)
		{
		// Draw select rectangle.
		m_renderer->SetMaterialColor( SELECTION_RECT_COLOR.x,SELECTION_RECT_COLOR.y,SELECTION_RECT_COLOR.z,1 ); // white
		m_renderer->DrawLine( Vec3(rc.left,rc.top,0),Vec3(rc.right,rc.top,0) );
		m_renderer->DrawLine( Vec3(rc.right,rc.top,0),Vec3(rc.right,rc.bottom,0) );
		m_renderer->DrawLine( Vec3(rc.right,rc.bottom,0),Vec3(rc.left,rc.bottom,0) );
		m_renderer->DrawLine( Vec3(rc.left,rc.bottom,0),Vec3(rc.left,rc.top,0) );
		}
		*/
	}

	if (!IsEquivalent(box.min,box.max,0))
	{
		switch (m_axis)
		{
		case VPA_XY:
		case VPA_YX:
			box.min.z = box.max.z = 0;
			break;
		case VPA_XZ:
			box.min.y = box.max.y = 0;
			break;
		case VPA_YZ:
			box.min.x = box.max.x = 0;
			break;
		}

		dc.PushMatrix( GetScreenTM() );
		dc.SetColor( SELECTION_RECT_COLOR.x,SELECTION_RECT_COLOR.y,SELECTION_RECT_COLOR.z,1 );
		dc.DrawWireBox( box.min,box.max );
		dc.PopMatrix();
	}

	if (!m_selectedRect.IsRectEmpty())
	{
		dc.SetColor( SELECTION_RECT_COLOR.x,SELECTION_RECT_COLOR.y,SELECTION_RECT_COLOR.z,1 );
		CPoint p1 = CPoint( m_selectedRect.left,m_selectedRect.top );
		CPoint p2 = CPoint( m_selectedRect.right,m_selectedRect.bottom );
		dc.DrawLine( Vec3(p1.x,p1.y,0),Vec3(p2.x,p1.y,0) );
		dc.DrawLine( Vec3(p1.x,p2.y,0),Vec3(p2.x,p2.y,0) );
		dc.DrawLine( Vec3(p1.x,p1.y,0),Vec3(p1.x,p2.y,0) );
		dc.DrawLine( Vec3(p2.x,p1.y,0),Vec3(p2.x,p2.y,0) );
	}
}

//////////////////////////////////////////////////////////////////////////
void C2DViewport::DrawViewerMarker( DisplayContext &dc )
{
	float noScale = 1.0f/GetZoomFactor();

	CViewport *pVP = GetViewManager()->GetGameViewport();
	if (!pVP)
		return;

	Ang3 viewAngles = Ang3::GetAnglesXYZ( Matrix33(pVP->GetViewTM()) );

	Matrix34 tm;
	switch (m_axis)
	{
	case VPA_XY:
	case VPA_YX:
		tm = Matrix34::CreateRotationXYZ(Ang3(0,0,viewAngles.z));
		break;
	case VPA_XZ:
		tm = Matrix34::CreateRotationXYZ(Ang3(viewAngles.x,viewAngles.y,viewAngles.z));
		break;
	case VPA_YZ:
		tm = Matrix34::CreateRotationXYZ(Ang3(viewAngles.x,0,viewAngles.z));
		break;
	}
	tm.SetTranslation( pVP->GetViewTM().GetTranslation() );

	dc.PushMatrix( GetScreenTM()*tm);

	Vec3 dim(MARKER_SIZE,MARKER_SIZE/2,MARKER_SIZE);
	dc.SetColor( RGB(0,0,255) ); // blue
	dc.DrawWireBox( -dim*noScale,dim*noScale );

	float fov = GetIEditor()->GetSystem()->GetViewCamera().GetFov();

	Vec3 q[4];
	float dist = 30;
	float ta = (float)tan(0.5f*fov);
	float h = dist * ta;
	float w = h * gSettings.viewports.fDefaultAspectRatio; //  ASPECT ??
	//float h = w / GetAspect();
	q[0] = Vec3( w,dist, h) * noScale;
	q[1] = Vec3(-w,dist, h) * noScale;
	q[2] = Vec3(-w,dist,-h) * noScale;
	q[3] = Vec3( w,dist,-h) * noScale;

	// Draw frustum.
	dc.DrawLine( Vec3(0,0,0),q[0] );
	dc.DrawLine( Vec3(0,0,0),q[1] );
	dc.DrawLine( Vec3(0,0,0),q[2] );
	dc.DrawLine( Vec3(0,0,0),q[3] );

	// Draw quad.
	dc.DrawPolyLine( q,4 );

	dc.PopMatrix();
}

//////////////////////////////////////////////////////////////////////////
void C2DViewport::DrawObjects( DisplayContext &dc )
{
	//dc.renderer->SetBlendMode();
	//dc.renderer->EnableBlend(true);
	dc.PushMatrix( GetScreenTM() );
	GetIEditor()->GetObjectManager()->Display( dc );

	// Display editing tool.
	if (GetEditTool())
	{
		GetEditTool()->Display( dc );
	}
	dc.PopMatrix();
}

//////////////////////////////////////////////////////////////////////////
void C2DViewport::DrawBrush( DisplayContext &dc,struct SBrush *brush,const Matrix34 &brushTM,int flags )
{
	float fScale = GetZoomFactor();

	//Matrix tm = brushTM * GetViewTM();
	//glMultMatrixf( (float*)(&tm) );
	dc.PushMatrix( brushTM );

	// if selected.
	bool bSelected = flags == 1;

	if (bSelected)
	{
		dc.SetLineWidth(2);
		//glEnable(GL_LINE_STIPPLE);
		//glLineStipple( 10,0xAAAA );
	}

	for (size_t i = 0; i < brush->GetNumberOfFaces(); ++i)
	{	
		// if (f->m_Plane.normal[0] <= 0)
		//if (f->m_Plane.normal[m_cullAxis] <= 0)
		//continue;

		//CHANGED_BY_IVO
		//Vec3 norm = brushTM.TransformVector( f->m_Plane.normal );
		Vec3 norm = brushTM * ( brush->GetFaceNormal(i) );

		if (norm[m_cullAxis] <= 0)
			continue;

		if (!brush->IsValidFace(i))
			continue;

		int numv = brush->GetNumberOfFacePoints(i);

		//glBegin(GL_LINE_LOOP);
		for( int j=0; j < numv; j++)
		{
			int k = ((j+1) < numv) ? j+1 : 0;
			dc.DrawLine( brush->GetFacePointPos(i,j), brush->GetFacePointPos(i,k) );

			//glVertex3fv( &brush->GetFacePointPos(i,j) );
			//glVertex3fv( &brush->GetFacePointPos(i,k) );
		}
		//glEnd();
	}

	if (bSelected)
	{
		//glDisable(GL_LINE_STIPPLE);
		dc.SetLineWidth(0);
	}

	dc.PopMatrix();

	/*
	if (bSelected)
	{
	int ix = 0;
	int iy = 0;
	switch (m_axis)
	{
	case VPA_XY:
	ix = 0; iy = 1;
	break;
	case VPA_YX:
	ix = 1; iy = 0;
	break;
	case VPA_XZ:
	ix = 0; iy = 2;
	break;
	case VPA_YZ:
	ix = 1; iy = 2;
	break;
	}
	Vec3 p1(brush->m_bounds.min[ix],brush->m_bounds.min[iy],0);
	Vec3 p2(brush->m_bounds.max[ix],brush->m_bounds.max[iy],0);
	}
	*/
}

//////////////////////////////////////////////////////////////////////////
void C2DViewport::DrawTextLabel( DisplayContext &dc,const Vec3& pos,float size,const ColorF& color,const char *text, const bool bCenter )
{
	float fCol[4] = { color.r,color.g,color.b,color.a };
	//Vec3 p = GetScreenTM().TransformPoint( pos );
	m_renderer->Draw2dLabel( pos.x,pos.y,size,fCol,bCenter,"%s",text);
}

//////////////////////////////////////////////////////////////////////////
void C2DViewport::MakeConstructionPlane( int axis )
{
	RefCoordSys coordSys = GetIEditor()->GetReferenceCoordSys();
	m_constructionMatrix[COORDS_VIEW] = GetViewTM();

	Vec3 p(0,0,0);

	switch (m_axis)
	{
	case VPA_XY:
		//m_constructionMatrix[COORDS_VIEW].SetFromVectors( Vec3(1,0,0),Vec3(0,1,0),Vec3(0,0,1),Vec3(0,0,0) );
		AssignConstructionPlane( p,p+Vec3(0,1,0),p+Vec3(1,0,0) );
		break;
	case VPA_YX:
		//m_constructionMatrix[COORDS_VIEW].SetFromVectors( Vec3(1,0,0),Vec3(0,1,0),Vec3(0,0,1),Vec3(0,0,0) );
		m_constructionPlane.SetPlane( p,p+Vec3(1,0,0),p+Vec3(0,1,0) );
		break;
	case VPA_XZ:
		//m_constructionMatrix[COORDS_VIEW].SetFromVectors( Vec3(1,0,0),Vec3(0,0,1),Vec3(0,1,0),Vec3(0,0,0) );
		AssignConstructionPlane( p,p+Vec3(0,0,1),p+Vec3(1,0,0) );
		break;
	case VPA_YZ:
		//m_constructionMatrix[COORDS_VIEW].SetFromVectors( Vec3(0,0,1),Vec3(1,0,0),Vec3(0,1,0),Vec3(0,0,0) );
		AssignConstructionPlane( p,p+Vec3(0,0,1),p+Vec3(0,1,0) );
		break;
	}
}

//////////////////////////////////////////////////////////////////////////
const Matrix34& C2DViewport::GetConstructionMatrix( RefCoordSys coordSys )
{
	if (coordSys == COORDS_VIEW)
		return GetViewTM();
	return m_constructionMatrix[coordSys];
}

//////////////////////////////////////////////////////////////////////////
AABB C2DViewport::GetWorldBounds( CPoint pnt1,CPoint pnt2 )
{
	Vec3 org;
	AABB box;
	box.Reset();
	box.Add( ViewToWorld( pnt1 ) );
	box.Add( ViewToWorld( pnt2 ) );

	int maxSize = MAX_WORLD_SIZE;
	switch (m_axis)
	{
	case VPA_XY:
	case VPA_YX:
		box.min.z = -maxSize;
		box.max.z = maxSize;
		break;
	case VPA_XZ:
		box.min.y = -maxSize;
		box.max.y = maxSize;
		break;
	case VPA_YZ:
		box.min.x = -maxSize;
		box.max.x = maxSize;
		break;
	}
	return box;
}

//////////////////////////////////////////////////////////////////////////
void C2DViewport::OnDragSelectRectangle( CPoint pnt1,CPoint pnt2,bool bNormilizeRect )
{
	Vec3 org;
	AABB box;
	box.Reset();

	Vec3 p1 = ViewToWorld( pnt1 );
	Vec3 p2 = ViewToWorld( pnt2 );
	org = p1;

	// Calculate selection volume.
	box.Add( p1 );
	box.Add( p2 );

	int maxSize = MAX_WORLD_SIZE;

	char szNewStatusText[512];
	float w,h;

	switch (m_axis)
	{
	case VPA_XY:
		box.min.z = -maxSize;
		box.max.z = maxSize;

		w = box.max.x - box.min.x;
		h = box.max.y - box.min.y;
		sprintf(szNewStatusText, "X:%g Y:%g W:%g H:%g",org.x,org.y,w,h );
		break;
	case VPA_YX:
		box.min.z = -maxSize;
		box.max.z = maxSize;

		w = box.max.y - box.min.y;
		h = box.max.x - box.min.x;
		sprintf(szNewStatusText, "X:%g Y:%g W:%g H:%g",org.x,org.y,w,h );
		break;
	case VPA_XZ:
		box.min.y = -maxSize;
		box.max.y = maxSize;

		w = box.max.x - box.min.x;
		h = box.max.z - box.min.z;
		sprintf(szNewStatusText, "X:%g Z:%g  W:%g H:%g",org.x,org.z,w,h );
		break;
	case VPA_YZ:
		box.min.x = -maxSize;
		box.max.x = maxSize;

		w = box.max.y - box.min.y;
		h = box.max.z - box.min.z;
		sprintf(szNewStatusText, "Y:%g Z:%g  W:%g H:%g",org.y,org.z,w,h );
		break;
	}

	GetIEditor()->SetSelectedRegion( box );

	// Show marker position in the status bar
	GetIEditor()->SetStatusText(szNewStatusText);
}

//////////////////////////////////////////////////////////////////////////
bool C2DViewport::HitTest( CPoint point,HitContext &hitInfo )
{
	hitInfo.bounds = &m_displayBounds;
	return CViewport::HitTest( point,hitInfo );
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
bool C2DViewport::IsBoundsVisible( const AABB &box ) const
{
	// If at least part of bbox is visible then its visible.
	if (m_displayBounds.IsIntersectBox( box ))
		return true;
	return false;
}

//////////////////////////////////////////////////////////////////////////
void C2DViewport::CenterOnSelection()
{
	CSelectionGroup *sel = GetIEditor()->GetSelection();
	if (sel->IsEmpty())
		return;

	//SetZoomFactor(1);

	AABB bounds = sel->GetBounds();
	Vec3 selPos = sel->GetCenter();

	float size = (bounds.max-bounds.min).GetLength();

	//CPoint p1 = WorldToView(bounds.min);
	//CPoint p2 = WorldToView(bounds.max);

	Vec3 v1 = ViewToWorld( CPoint(m_rcClient.left,m_rcClient.bottom) );
	Vec3 v2 = ViewToWorld( CPoint(m_rcClient.right,m_rcClient.top) );
	Vec3 vofs = (v2-v1) * 0.5f;
	selPos -= vofs;
	SetOrigin2D( selPos );

	m_bContentValid = false;

	//SetZoomFactor( size / 10 );

	/*
	CSelectionGroup *sel = GetIEditor()->GetSelection();
	BBox bounds = sel->GetBounds();
	Vec3 selPos = sel->GetCenter();
	float size = GetLength(bounds.max - bounds.min);
	Vec3 pos = selPos;
	pos += Vec3(0,size*2,size);
	//pos.z = GetIEditor()->GetTerrainElevation(pos.x,pos.y)+5;
	GetIEditor()->SetViewerPos( pos );
	Vec3 dir = GetNormalized(selPos - pos);
	dir = ConvertVectorToCameraAngles(dir);
	GetIEditor()->SetViewerAngles( dir );
	*/
}

//////////////////////////////////////////////////////////////////////////
Vec3 C2DViewport::GetOrigin2D() const
{
	if (gSettings.viewports.bSync2DViews)
		return GetViewManager()->GetOrigin2D();
	else
		return m_origin2D;
}

//////////////////////////////////////////////////////////////////////////
void C2DViewport::SetOrigin2D( const Vec3 &org )
{
	m_origin2D = org;
	if (gSettings.viewports.bSync2DViews)
		GetViewManager()->SetOrigin2D( m_origin2D );
}
